
#include "fge.h"

runtime_info run_info;

stack_t stack[MAX_STACK_DEPTH], mem[MAX_MEM_DEPTH];

int g_stack_index = 0;

int g_lines = 0;
int g_now_index = -1;

bool g_has_size = false;

char filename[128];

vector < int > g_label;
vector <instruction_type> g_code;

inline bool next(char *&p)
{
	while (p[0]==' ' || p[0]=='\t' || p[0]=='\0') { if (p[0]=='\0') return false; else p++; }
	while ((p[0]!=' ' && p[0]!='\t') || p[0]=='\0') { if (p[0]=='\0') return false; else p++; }
	while (p[0]==' ' || p[0]=='\t' || p[0]=='\0') { if (p[0]=='\0') return false; else p++; }
	return true;
}

inline int find_name(const char* name, int count)
{
	const func_t *p = FUNC_TABLE;
	while (p->id != -1)
	{
		if (!strcmp(p->name, name) && p->ins_count == count) return p->id;
		p++;
	}
	return -1;
}

int main(int argc, char *argv[])
{
	printf("Functional Grapher Excuter v1.5.0 Final Release\n\n");

	// handle arguments
	bool has_input = false;
	memset(&run_info, 0x0, sizeof(run_info));

	for (int i=1;i<argc;i++)
	{
		if (argv[i][0] == '-')
		{
			switch (argv[i][1])
			{
			case 'h':
			case 'H':
			case '?':
				printf("fge [drive:][path]filename [options]\n\n");
				printf("Options:\n");
				printf("  -debug              Show all debug messages for every instruction\n");
				printf("  -s <instruction>    Show debug messages for the specific standard instruction\n");
				printf("  -e <instruction>    Show debug messages for the specific extended instruction\n");
				printf("\n");
				printf("  -t                  Trace the instruction specificed above\n");
				printf("\n");
				printf("  -h                  Show this information\n");
				return 0;
			case 't':
			case 'T':
				run_info.t = true;
				break;
			case 'd':
			case 'D':
				run_info.d = true;
				break;
			case 's':
			case 'S':
				for (int j=0; j<3; j++)
				{
					int t = find_name(argv[i] + 2, j);
					if (t != -1)
					{
						run_info.s[t] = true;
					}
				}
				break;
			case 'e':
			case 'E':
				for (int j=0; j<3; j++)
				{
					int t = find_name(argv[i] + 2, j);
					if (t != -1)
					{
						run_info.e[t] = true;
					}
				}
				break;
			}
		}
		if (!has_input)
		{
			has_input = true;
			memset(filename, 0x0, sizeof(filename));
			strcpy(filename, argv[i]);
		}
	}
	if (!has_input)
	{
		printf("No input file\n");
		return 0;
	}
	// open file
	FILE* fp;
	if (!(fp=fopen(filename,"r")))
	{
		char extfile[200];
		strcpy(extfile,filename);
		strcat(extfile,".fgo");
		if (!(fp=fopen(extfile,"r")))
		{
			printf("Can not open file '%s' or '%s'\n",filename,extfile);
			return 0;
		}
		strcpy(filename,extfile);
	}

	// read from file
	printf("Mounting file ...\n");
	char buf[1024];
	map< int , int > *p_labelmap =  new map< int , int >;
	int max_label = 0;
	while (fgets(buf,sizeof(buf)-1,fp))
	{
		char *p = buf;
		if (isdigit(p[0]))
		{
			int label;
			sscanf(p,"%d:",&label);
			if (!next(p)) error("instruction request after label '%d'",label);
			if (max_label < label) max_label = label;
			if (p_labelmap->count(label))
			{
				error("label %d already existed",label);
			}
			else
			{
				(*p_labelmap)[label] = g_lines;
			}
		}

		instruction_type ins;
		memset(&ins, 0x0, sizeof(ins));
		char name_buf[20];
		if (1 != sscanf(p,"%s",name_buf)) continue; //empty line , skip
		int ins_para_index = 0;
		while (next(p))
		{
			if (ins_para_index>=3) error("too much param for instruction '%s'",name_buf);
			char buf_t[1024];
			if (1 != sscanf(p, "%s", buf_t)) error("system error in reading data");
			bool temp_is_int = (strchr(buf_t,'.') == NULL);
			if (buf_t[0]=='!')
			{
				if (temp_is_int)
					sscanf(buf_t+1,"%d",&ins.param[ins_para_index].i);
				else
					sscanf(buf_t+1,"%f",&ins.param[ins_para_index].f);
				ins.is_mem[ins_para_index] = false;
			}
			else
			{
				if (temp_is_int)
					sscanf(buf_t,"%d",&ins.param[ins_para_index].i);
				else
					sscanf(buf_t,"%f",&ins.param[ins_para_index].f);
				ins.is_mem[ins_para_index] = true;
			}
			ins_para_index++;
		}
		if ((ins.name = find_name(name_buf, ins_para_index))==-1)
			error("instruction '%s' can not have %d syntax",name_buf,ins_para_index);
		g_code.push_back(ins);

		g_lines ++;
	}
	// close file
	fclose(fp);

	g_label.resize(max_label+1, -1);
	for (map<int, int>::iterator i = p_labelmap->begin(); i != p_labelmap->end(); i++)
	{
		g_label[i->first] = i->second;
	}
	delete p_labelmap;

	if (!init()) { printf("Init failed\n"); return 0;}
	printf("Starting object '%s' ...\n",filename);
	sim();
	printf("\nSimulation finished, starting rendering ...\n");
	return run();
}

void error(const char *format, ...)
{
    va_list ap;
    va_start(ap, format);
    char buf[100];
    vsprintf(buf, format, ap);
	fprintf(stderr,"In Line %d:\n",(g_now_index==-1?g_lines:g_now_index));
	fprintf(stderr,"%s\n",buf);
	system("pause");
	exit(0);
}
